﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EffiProz.Core.Lib;
using EffiProz.Core.DataTypes;
using EffiProz.Core.Indexes;
using EffiProz.Core.Errors;
using EffiProz.Core.Persist;
using EffiProz.Core.Results;
using EffiProz.Core.Rights;
using EffiProz.Core.ScriptIO;

namespace EffiProz.Core
{
    public class StatementCommand : Statement
    {
        public Expression[] expressions;
        public Object[] parameters;

        public StatementCommand(int type, Object[] args, QNameManager.QName readName,
                         QNameManager.QName writeName)
            : base(type)
        {

            this._isTransactionStatement = true;
            this.parameters = args;

            if (readName != null)
            {
                this.readTableNames = new QNameManager.QName[] { readName };
            }

            if (writeName != null)
            {
                this.writeTableNames = new QNameManager.QName[] { writeName };
            }

            switch (type)
            {

                case StatementTypes.DATABASE_CHECKPOINT:
                    group = StatementTypes.X_HSQLDB_OPERATION;
                    _isLogged = false;
                    break;

                case StatementTypes.DATABASE_BACKUP:
                case StatementTypes.DATABASE_SCRIPT:
                    group = StatementTypes.X_HSQLDB_OPERATION;
                    _isLogged = false;
                    break;

                case StatementTypes.SET_DATABASE_UNIQUE_NAME:
                case StatementTypes.SET_DATABASE_FILES_WRITE_DELAY:
                case StatementTypes.SET_DATABASE_FILES_TEMP_PATH:
                    this._isTransactionStatement = false;
                    group = StatementTypes.X_HSQLDB_SETTING;
                    break;

                //
                case StatementTypes.SET_DATABASE_DEFAULT_INITIAL_SCHEMA:
                case StatementTypes.SET_DATABASE_DEFAULT_TABLE_TYPE:
                case StatementTypes.SET_DATABASE_FILES_CACHE_ROWS:
                case StatementTypes.SET_DATABASE_FILES_CACHE_SIZE:
                case StatementTypes.SET_DATABASE_FILES_SCALE:
                case StatementTypes.SET_DATABASE_FILES_DEFRAG:
                case StatementTypes.SET_DATABASE_FILES_EVENT_LOG:
                case StatementTypes.SET_DATABASE_FILES_LOCK:
                case StatementTypes.SET_DATABASE_FILES_LOG_SIZE:
                case StatementTypes.SET_DATABASE_FILES_NIO:
                case StatementTypes.SET_DATABASE_FILES_SCRIPT_FORMAT:
                case StatementTypes.SET_DATABASE_PROPERTY:
                case StatementTypes.SET_DATABASE_RESULT_MEMORY_ROWS:
                case StatementTypes.SET_DATABASE_SQL_IGNORECASE:
                case StatementTypes.SET_DATABASE_SQL_REFERENTIAL_INTEGRITY:
                case StatementTypes.SET_DATABASE_SQL_STRICT_NAMES:
                case StatementTypes.SET_DATABASE_SQL_STRICT_SIZE:
                case StatementTypes.SET_DATABASE_TRANSACTION_CONTROL:
                case StatementTypes.SET_DATABASE_GC:

                //
                case StatementTypes.SET_DATABASE_SQL_COLLATION:
                case StatementTypes.SET_DATABASE_FILES_BACKUP_INCREMENT:
                case StatementTypes.SET_DATABASE_SCRIPT_FORMAT:
                case StatementTypes.SET_DATABASE_TEXT_SOURCE:
                    group = StatementTypes.X_HSQLDB_SETTING;
                    this._isTransactionStatement = true;
                    break;

                case StatementTypes.SET_TABLE_SOURCE_HEADER:
                    _isLogged = false;
                    goto case StatementTypes.SET_TABLE_SOURCE;
                case StatementTypes.SET_TABLE_SOURCE:
                    metaDataImpact = Statement.META_RESET_VIEWS;
                    group = StatementTypes.X_HSQLDB_OPERATION;
                    this._isTransactionStatement = true;
                    break;

                case StatementTypes.SET_TABLE_READONLY:
                    metaDataImpact = Statement.META_RESET_VIEWS;
                    group = StatementTypes.X_HSQLDB_SETTING;
                    this._isTransactionStatement = true;
                    break;

                case StatementTypes.DATABASE_SHUTDOWN:
                    _isLogged = false;
                    group = StatementTypes.X_HSQLDB_OPERATION;
                    this._isTransactionStatement = false;
                    break;

                case StatementTypes.SET_TABLE_TYPE:
                    group = StatementTypes.X_HSQLDB_OPERATION;
                    this._isTransactionStatement = true;
                    break;

                case StatementTypes.SET_TABLE_INDEX:
                    group = StatementTypes.X_HSQLDB_SETTING;
                    this._isTransactionStatement = false;
                    _isLogged = false;
                    break;

                case StatementTypes.SET_USER_INITIAL_SCHEMA:
                case StatementTypes.SET_USER_PASSWORD:
                    group = StatementTypes.X_HSQLDB_SETTING;
                    this._isTransactionStatement = false;
                    break;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500,
                                             "StatementCommand");
            }
        }

        public override Result execute(Session session)
        {

            Result result;

            try
            {
                result = getResult(session);
            }
            catch (Exception t)
            {
                result = Result.newErrorResult(t, null);
            }

            if (result.isError())
            {
                result.getException().setStatementType(group, type);

                return result;
            }

            try
            {
                if (_isLogged)
                {
                    session.database.logger.writeToLog(session, sql);
                }
            }
            catch (Exception e)
            {
                return Result.newErrorResult(e, sql);
            }

            return result;
        }

       public Result getResult(Session session) {

        if (this.isExplain) {
            return Result.newSingleColumnStringResult("OPERATION",
                    describe(session));
        }

        switch (type) {

            case StatementTypes.DATABASE_BACKUP : {
                String  path       = ((String) parameters[0]);
                bool blocking   = Convert.ToBoolean( parameters[1]);
                bool script = Convert.ToBoolean(parameters[2]);
                bool compressed = Convert.ToBoolean(parameters[3]);

                try {
                    session.checkAdmin();

                    if (!session.database.getType().Equals(
                            DatabaseURL.S_FILE)) {

                        // Do not enforce this constraint for SCRIPT type
                        // backup.
                        return Result.newErrorResult(
                            Error.error(ErrorCode.DATABASE_IS_NON_FILE));

                        // If we were to back up res: type DB's, could use
                        // DatabasURL.isFileBasedDataType(), but I see no
                        // point to back up one of these.
                    }

                    if (session.database.isReadOnly()) {

                        // Do not enforce this constraint for SCRIPT type
                        // backup.
                        return Result.newErrorResult(
                            Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY),
                            null);
                    }

                    session.database.logger.backup(path,
                                                   session.database.getPath(),
                                                   script, blocking,
                                                   compressed);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.DATABASE_CHECKPOINT : {
                bool defrag = Convert.ToBoolean(parameters[0]);

                try {
                    session.database.logger.checkpoint(defrag);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_BACKUP_INCREMENT : {
                try {
                    bool mode = Convert.ToBoolean(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setIncrementBackup(mode);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_CACHE_ROWS : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setCacheMaxRows(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_CACHE_SIZE : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setCacheSize(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_SCALE : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setCacheFileScale(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_DEFRAG : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setDefagLimit(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_EVENT_LOG : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setEventLogLevel(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_LOCK : {
                try {

                    // no-op - to remove from release version
                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_NIO : {
                try {
                    bool value = Convert.ToBoolean(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setNioDataFile(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_LOG_SIZE : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setLogSize(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_TEMP_PATH : {
                try {
                    String value = (String) parameters[0];

                    session.checkAdmin();
                    session.checkDDLWrite();

                    // no action
                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_FILES_WRITE_DELAY : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.setMetaDirty(false);
                    session.database.logger.setWriteDelay(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_SQL_COLLATION : {
                try {
                    String name = (String) parameters[0];

                    /** @todo 1.9.0 - ensure no data in character columns */
                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.collation.setCollation(name);
                    session.database.setMetaDirty(false);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_SQL_IGNORECASE : {
                try {
                    bool mode = Convert.ToBoolean(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.setIgnoreCase(mode);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_SQL_REFERENTIAL_INTEGRITY : {
                bool mode = Convert.ToBoolean(parameters[0]);

                session.database.setReferentialIntegrity(mode);

                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_SQL_STRICT_NAMES : {
                bool mode = Convert.ToBoolean(parameters[0]);

                session.database.setStrictNames(mode);

                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_SQL_STRICT_SIZE : {
                bool mode = Convert.ToBoolean(parameters[0]);

                session.database.setStrictColumnSize(mode);

                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_DEFAULT_INITIAL_SCHEMA : {
                QNameManager.QName schema = (QNameManager.QName) parameters[0];

                //
                session.database.schemaManager.setDefaultSchemaHsqlName(
                    schema);
                session.database.setMetaDirty(true);

                //
                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_DEFAULT_TABLE_TYPE : {
                int type1 = Convert.ToInt32(parameters[0]);

                //
                session.database.schemaManager.setDefaultTableType(
                    type1);

                //
                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_SCRIPT_FORMAT : {
                try {
                    int value = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.logger.setScriptType(value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_TRANSACTION_CONTROL : {
                try {
                    int mode = Convert.ToInt32(parameters[0]);

                    session.checkAdmin();
                    session.checkDDLWrite();
                    session.database.txManager.setTransactionControl(session,
                            mode);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_GC : {
                try {
                    int count = Convert.ToInt32(parameters[0]); 

                    session.checkAdmin();

                   // JavaSystem.gcFrequency = count;

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_PROPERTY : {
                try {
                    String property = (String) parameters[0];
                    Object value    = parameters[1];

                    // command is a no-op from 1.9
                    session.checkAdmin();
                    session.checkDDLWrite();

                    /*
                    HsqlDatabaseProperties p =
                        session.database.getProperties();

                    p.setDatabaseProperty(property,
                                          value.ToString().toLowerCase());
                    */
                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_RESULT_MEMORY_ROWS : {
                int size = Convert.ToInt32(parameters[0]); 

                session.database.getProperties().setProperty(
                    EffiProzDatabaseProperties.effiproz_result_max_memory_rows,
                    size);
                session.database.setResultMaxMemoryRows(size);

                return Result.updateZeroResult;
            }
            case StatementTypes.SET_DATABASE_TEXT_SOURCE : {
                try {
                    String         source = (String) parameters[0];
                    EffiProzProperties props  = null;

                    if (source.Length > 0) {
                        props = EffiProzProperties.delimitedArgPairsToProps(source,
                                "=", ";", null);

                        if (props.getErrorKeys().Length > 0) {
                            throw Error.error(ErrorCode.TEXT_TABLE_SOURCE,
                                              props.getErrorKeys()[0]);
                        }
                    }

                    session.database.logger.setDefaultTextTableProperties(
                        source, props);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_DATABASE_UNIQUE_NAME : {
                try {
                    String name = (String) parameters[0];

                    session.database.setUniqueName(name);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.DATABASE_SCRIPT : {
                ScriptWriterText dsw  = null;
                String           name = (String) parameters[0];

                if (name == null) {
                    return session.database.getScript(false);
                } else {
                    try {
                        dsw = new ScriptWriterText(session.database, name,
                                                   true, true, true);

                        dsw.writeAll();
                        dsw.close();
                    } catch (CoreException e) {
                        return Result.newErrorResult(e, sql);
                    }

                    return Result.updateZeroResult;
                }
            }
            case StatementTypes.DATABASE_SHUTDOWN : {
                try {
                    int mode = Convert.ToInt32(parameters[0]); 

                    session.database.close(mode);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_TABLE_INDEX : {
                try {
                    QNameManager.QName name  = (QNameManager.QName) parameters[0];
                    String   value = (String) parameters[1];
                    Table table =
                        session.database.schemaManager.getTable(session,
                            name.name, name.schema.name);

                    table.setIndexRoots(session, value);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_TABLE_READONLY : {
                try {
                    QNameManager.QName name = (QNameManager.QName) parameters[0];
                    Table table =
                        session.database.schemaManager.getTable(session,
                            name.name, name.schema.name);
                    bool mode = Convert.ToBoolean(parameters[1]); 

                    session.checkAdmin();
                    session.checkDDLWrite();
                    table.setDataReadOnly(mode);
                    session.database.setMetaDirty(false);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_TABLE_SOURCE :
            case StatementTypes.SET_TABLE_SOURCE_HEADER : {
                try {
                    QNameManager.QName name = (QNameManager.QName) parameters[0];
                    Table table =
                        session.database.schemaManager.getTable(session,
                            name.name, name.schema.name);

                    if (!table.isText()) {
                        Exception e = Error.error(ErrorCode.X_S0522);

                        return Result.newErrorResult(e, sql);
                    }

                    if (parameters[1] != null) {
                        bool mode =
                            ((Boolean) Convert.ToBoolean(parameters[1])); 

                        if (mode) {
                            ((TextTable) table).connect(session);
                        } else {
                            ((TextTable) table).disconnect();
                        }

                        session.database.setMetaDirty(false);

                        return Result.updateZeroResult;
                    }

                    String  source = (String) parameters[2];
                    bool isDesc = Convert.ToBoolean(parameters[3]); 
                    bool isHeader =
                        Convert.ToBoolean(parameters[4]); 

                    if (isHeader) {
                        ((TextTable) table).setHeader(source);
                    } else {
                        ((TextTable) table).setDataSource(session, source,
                                                          isDesc, false);
                    }

                    return Result.updateZeroResult;
                } catch (Exception e) {
                    if (!(e is CoreException)) {
                        e = Error.error(ErrorCode.GENERAL_IO_ERROR,
                                        e.Message);
                    }

                    if (session.isProcessingLog()
                            || session.isProcessingScript()) {
                        session.addWarning((CoreException) e);
                        session.database.logger.logWarningEvent(
                            "Problem processing SET TABLE SOURCE", e);

                        return Result.updateZeroResult;
                    } else {
                        return Result.newErrorResult(e, sql);
                    }
                }
            }
            case StatementTypes.SET_TABLE_TYPE : {
                try {
                    QNameManager.QName name = (QNameManager.QName) parameters[0];
                    int      type2 = Convert.ToInt32(parameters[0]); 

                    //
                    session.checkAdmin();
                    session.checkDDLWrite();

                    Table table =
                        session.database.schemaManager.getUserTable(session,
                            name.name, name.schema.name);

                    session.setScripting(true);

                    TableWorks tw = new TableWorks(session, table);

                    tw.setTableType(session, type2);
                    session.database.setMetaDirty(false);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_USER_INITIAL_SCHEMA : {
                try {
                    User     user   = (User) parameters[0];
                    QNameManager.QName schema = (QNameManager.QName) parameters[1];

                    session.checkDDLWrite();

                    if (user == null) {
                        user = session.getUser();
                    } else {
                        session.checkAdmin();
                        session.checkDDLWrite();

                        user = session.database.userManager.get(
                            user.getNameString());
                    }

                    if (schema != null) {
                        schema =
                            session.database.schemaManager.getSchemaHsqlName(
                                schema.name);
                    }

                    //
                    user.setInitialSchema(schema);
                    session.database.setMetaDirty(false);

                    //
                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            case StatementTypes.SET_USER_PASSWORD : {
                try {
                    User   user = parameters[0] == null ? session.getUser()
                                                        : (User) parameters[0];
                    String password = (String) parameters[1];

                    session.checkDDLWrite();
                    session.setScripting(true);
                    user.setPassword(password);

                    return Result.updateZeroResult;
                } catch (CoreException e) {
                    return Result.newErrorResult(e, sql);
                }
            }
            default :
                throw Error.runtimeError(ErrorCode.U_S0500, "StatemntCommand");
        }
    }

        public override bool isAutoCommitStatement()
        {
            return _isTransactionStatement;
        }

        public override String describe(Session session)
        {
            return sql;
        }
    }
}
